/**
 * 
 */
package com.gragra.sampling.sequentialStructure;
import java.util.Arrays;
import java.util.HashSet;
import com.gragra.structures.Structure;
import com.gragra.util.StringToInt;
/**
 * contains word and tag information for a sequence of words represented as
 * codes that can be reversed with the method StringToInt.reverse and associated tag
 * codes that may also be reversed with the same method, both assumptions are
 * not enforced by the class itself. If there is no tag given for a word (only possible with the
 * paired constructor), then -1 is used to indicate this
 * @author Christoph Teichmann
 * created Feb 3, 2013 2:06:34 PM
 * @version 0.1
 */
public class WordAndTagHolder implements Structure
{
	/**
	 * 
	 */
	private ExtensionInformationHolder eih = null;
	/**
	 * @return the length
	 */
	public int getLength()
	{return length;}
	/**
	 * @return the tags
	 */
	public int[] getTags()
	{return tags;}
	/**
	 * @return the words
	 */
	public int[] getWords()
	{return words;}
	/**
	 * 
	 */
	private final int length;
	/**
	 * @param tags
	 * @param words
	 */
	public WordAndTagHolder(int[] tags, int[] words)
	{
		assert(tags.length == words.length);
		this.tags = tags;
		this.words = words;
		length = tags.length;
	}
	/**
	 * @param is
	 */
	public WordAndTagHolder(int[][] is)
	{
		this.tags = new int[is.length];
		this.words = new int[is.length];
		this.length = is.length;
		for(int i=0;i<this.length;++i)
		{
			int[] sub = is[i];
			this.words[i] = sub[0];
			if(sub.length > 1)
			{this.tags[i] = sub[1];}
			else
			{this.tags[i] = -1;}
			int l = sub.length-1;
			for(int k=2;k < l;k+=2)
			{addInformation(i,sub[k],sub[k+1]);}
		}
	}
	/**
	 * copy constructor that makes a SHALLOW copy of the original structure, this is intended for context in which one has a WordAndTagHolder
	 * wth_1 and wants to create some derived structure wth_2 from it, the intended use is to throw away wth_1 after wth_2 has been constructed
	 * @param wth
	 */
	public WordAndTagHolder(WordAndTagHolder wth)
	{
		this.tags = wth.tags;
		this.words = wth.words;
		this.length = wth.length;
		this.eih = wth.eih;
	}
	/**
	 *
	 * @param i
	 * @param j
	 */
	private void addInformation(int pos,int level, int value)
	{
		ExtensionInformationLevel el = ExtensionLevels.values()[level];
		if(el.isFinite())
		{this.getExtensionInformation().addInformation(pos, el, el.getPossibleValues()[value]);}
		else
		{this.getExtensionInformation().addInformation(pos, el, value);}
	}
	/**
	 * 
	 *
	 * @param from
	 * @param to
	 * @return
	 */
	public int[] getTags(int from,int to)
	{return Arrays.copyOfRange(tags, from, to);}
	/**
	 * 
	 *
	 * @param from
	 * @param to
	 * @return
	 */
	public int[] getWords(int from,int to)
	{return Arrays.copyOfRange(words, from, to);}
	/**
	 * 
	 */
	private final int[] tags;
	/**
	 * 
	 */
	private final int[] words;
	/**
	 * 
	 *
	 * @return
	 */
	public int getTag(int pos)
	{return this.tags[pos];}
	/**
	 * 
	 *
	 * @param pos
	 * @return
	 */
	public int getWord(int pos)
	{return this.words[pos];}
	/**
	 * 
	 *
	 * @return
	 */
	public ExtensionInformationHolder getExtensionInformation()
	{return this.eih == null ? (this.eih = new ExtensionInformationHolder(getLength())) : this.eih;}
	/**
	 * returns a string representation of the word at the given position, it is just a shorthand for
	 * StringToInt.reverse(this.getWord(position))
	 * @param position
	 * @return
	 */
	public String getWordString(int position)
	{return StringToInt.reverse(this.words[position]);}
	/**
	 * returns a string representation of the Tag at the given position, it is just a shorthand for
	 * StringToInt.reverse(this.getTag(position)) if the tag transfers to null, then it uses NONE
	 * @param position
	 * @return
	 */
	public String getTagString(int position)
	{
		String s = StringToInt.reverse(this.tags[position]); 
		return s == null ? "NONE" : s;
	}
	/**
	 * 
	 * @param pos
	 * @param to
	 */
	public void reduceAnnotation(int pos, HashSet<ExtensionInformationLevel> to)
	{
		this.getExtensionInformation().reduceFiniteInformation(pos,to);
		this.getExtensionInformation().reduceInfiniteInformation(pos,to);
	}
}